import { parse } from '@babel/parser'
import traverse from '@babel/traverse'
import generate from '@babel/generator'
import * as types from '@babel/types'
import fs from 'node:fs'
import path from 'node:path'

const fileName = path.resolve(__dirname, '../demo/02.tsx')

const source = fs.readFileSync(fileName, {
	encoding: 'utf-8',
})

// 转换成AST
const ast = parse(source, {
	// 代码源码是 ts版本的jsx, 所以需要使用 typescript插件
	plugins: ['typescript', 'jsx'],
})

traverse(ast, {
	// 我们要修改的是，函数的调用， console.log 本质就是函数的调用，所以，这里要操作的就是 CallExpression 这个hook
	// traverse 当遇到 AST中类型为 函数调用的时候， CallExpression 这个钩子就会执行
	// 所有的函数调用，都会走这个方法，所以，这个钩子本身是会执行N次， 看源代码中有多少函数调用
	CallExpression(path) {
		// 还原之前是什么代码
		const calleeStr = generate(path.node.callee)

		// console.log('原始代码的样子（函数调用）: ', calleeStr.code)

		// 假设： 需要拦截  console.log 和console.error ， 将里面加上 行和列信息

		// 可以通过还原后的代码判断，也可以使用AST节点中的信息判断

		if (['console.log', 'console.error'].includes(calleeStr.code)) {
			// 拿到行列号
			if (path.node.loc && path.node.loc.start) {
				// 拿到起点的行和列
				const { line, column } = path.node.loc.start

				// 拿到原来的参数
				let args = path.node.arguments

				// 创建一个我们希望加的参数
				const newArg = types.stringLiteral(
					`02.jsx, (行:${line}, 列：${column})`
				)

				// 对原始AST做改动, 把新增的自定义内容，添加到原来的函数参数的第一个
				args.unshift(newArg)
			}
		}

		let a = 2
		if (a === 1) {
			// 或者直接通过 AST里面的信息，有原始值
			let callee = path.node.callee

			if (callee.type === 'MemberExpression') {
				const property = callee.property
				const object = callee.object

				// console.log(callee)

				if (object.type === 'Identifier' && object.name === 'console') {
					// log是 console的属性，在 callee对象的 property 对象上面有描述信息
					if (
						property.type === 'Identifier' &&
						(property.name === 'log' || property.name === 'error')
					)
						// 一定要判断type类型为  Identifier 标识符， 因为可能有其他的类型

						// todo， 这里就跟上面的将代码变成字符串，再比较字符串的逻辑是一样的
						console.log()
				}
			}
		}
	},
})

let { code } = generate(ast, {
	jsescOption: {
		// 保留编码格式
		minimal: true,
	},
})

let dir = path.resolve(__dirname, '../demo/02-1.tsx')
fs.writeFileSync(dir, code)
